package com.sh.weblogspringboot.jwt.util;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.UnsupportedJwtException;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SignatureException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.stereotype.Component;

import java.security.Key;
import java.sql.Date;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Base64;

/**
 * @author songHai
 * @Classname JwtTokenHelper
 * @Description JwtTokenHelper 工具类
 * @Date 2025/3/26 1:41
 */
@Component
@Slf4j
public class JwtTokenHelper implements InitializingBean {

    /**
     * 签发人。
     */
    @Value("${jwt.issuer}")
    private String issuser;

    /**
     * Token 失效时间（分钟）
     */
    @Value("${jwt.tokenExpireTime}")
    private Long tokenExpireTime;

    /**
     * 秘钥.
     */
    private Key key;

    /**
     * JWT解析.
     */
    private JwtParser jwtParser;

    /**
     * 解码配置文件中配置的 Base 64 编码 key 为秘钥.
     *
     * @param base64Key
     */
    @Value("${jwt.secret}")
    public void setBase64Key(String base64Key) {
        key = Keys.hmacShaKeyFor(Base64.getDecoder().decode(base64Key));
    }


    /**
     * 初始化JwtParser.
     *
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        // 考虑到不同服务器之间可能存在时钟偏移，setAllowedClockSkewSeconds 用于设置能够容忍的最大的时钟误差
        jwtParser = Jwts.parserBuilder()
                .requireIssuer(issuser)
                .setSigningKey(key)
                .setAllowedClockSkewSeconds(10).build();
    }

    /**
     * 生成Token.
     *
     * @param userName
     * @return
     */
    public String generateToken(String userName) {
        LocalDateTime now = LocalDateTime.now();
        // 设置Token失效时间
        LocalDateTime expireTime = now.plusMinutes(tokenExpireTime);

        return Jwts.builder().setSubject(userName)
                .setIssuer(issuser)
                .setIssuedAt(Date.from(now.atZone(ZoneId.systemDefault()).toInstant()))
                .setExpiration(Date.from(expireTime.atZone(ZoneId.systemDefault()).toInstant()))
                .signWith(key)
                .compact();
    }

    /**
     * 解析Token.
     *
     * @param token
     * @return
     */
    public Jws<Claims> parseToken(String token) {
        try {
            return jwtParser.parseClaimsJws(token);
        } catch (SignatureException | MalformedJwtException | UnsupportedJwtException | IllegalArgumentException e) {
            throw new BadCredentialsException("Token 不可用!", e);
        } catch (ExpiredJwtException e) {
            throw new CredentialsExpiredException("Token 已失效!", e);
        }
    }

    /**
     * 校验 Token 是否可用， 如果抛异常这表示失败.
     *
     * @param token
     * @return
     */
    public void validateToken(String token) {
        jwtParser.parseClaimsJws(token);
    }

    /**
     * 解析 Token 获取用户名
     *
     * @param token
     * @return
     */
    public String getUserNameFromToken(String token) {
        try {
            Claims claims = jwtParser.parseClaimsJws(token).getBody();
            String userName = claims.getSubject();
            return userName;
        } catch (Exception e) {
            log.error("JwtTokenHelper-->getUserNameFromToken获取用户名失败， 返回用户为null。");
        }
        return null;
    }


    /**
     * 测试:生成一个 Base64 的安全秘钥
     * 专门用于生成一个 Base64 的安全秘钥，执行 main() 方法即可，然后将生成好的秘钥配置到 yml 文件中.
     *
     * @return
     */
    private static String generateBase64Key() {
        // 生成安全秘钥
        Key secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS512);

        // 将秘钥进行base64编码

        return Base64.getEncoder().encodeToString(secretKey.getEncoded());
    }

    public static void main(String[] args) throws Exception {
        System.out.println("生成的秘钥为：" + generateBase64Key());

    }
}
